package com.htfyun.hardware.reflect;

import android.annotation.SuppressLint;
import android.util.Log;

import java.lang.reflect.Method;
import java.util.Arrays;

public class ClassReflect {
    public final static String TAG = "ClassReflect";
    public final static int MAX_SUPER_LEVEL = 100;

    private static boolean DEBUG = false ; // true;


    @SuppressLint("NewApi") public static <T> T[] concat(T[] first, T[] second) {
        T[] result = Arrays.copyOf(first, first.length + second.length);
        System.arraycopy(second, 0, result, first.length, second.length);
        return result;
    }

    @SuppressLint("NewApi") public static Method[] concatNoDup(Class<?> theCalss,Method[] first,Method[] second) {
        Method[] temp =  new Method[second.length]; // Arrays.copyOf(first, first.length + second.length);
        int j,idx_second,idx_added;
        boolean dup;

        String classPath = theCalss.getName();
        idx_added = 0;
        for( idx_second= 0; idx_second < second.length; idx_second++){
            Method m = second[idx_second];
            dup = false;
            for(j = 0 ; j < first.length; j++ ){
                if( first[j].equals(m)) {
                    if( DEBUG ) Log.d(TAG,"DUP Method:" + m.getName());
                    dup = true;
                    break;
                }
            }
            if( !dup ) {
                temp[idx_added++] = m;
                if( DEBUG ) Log.d(TAG, classPath + ":" + m.getName());
            }
        }
        Method[] result = Arrays.copyOf(first, first.length + idx_added);
        System.arraycopy(temp, 0, result, first.length, idx_added);
        return result;
    }

    /*
     * get the methods of a class
     * @param theCalss  -- the class whitch we want to get
     * @param getSuper  -- weather we need to get is's super class metheds.
     * @param getPublic -- true: only get public methods, false: get all methods.
     * @return  -- the methods array,null is NO.
     */
    public static Method[] GetClassPublicMethodsClass(Class<?> theCalss, boolean getSuper,
                                                               boolean getPublic ,int maxSuper) {
        Method[] allMethods = null;

        //"android.os.ServiceManager"
        //theCalss = Class.forName(classPath);
        while( theCalss != null && !theCalss.getName().equals("java.lang.Object")) {
            /*
             * get the only public methods.
             */
            Method[] theMethods;
            if( getPublic ) {
                theMethods = theCalss.getMethods();
            } else {
                theMethods = theCalss.getDeclaredMethods();
            }
            if( theMethods != null ) {
                if (allMethods == null) {
                    allMethods = theMethods;
                } else {
                    allMethods = concatNoDup(theCalss,allMethods, theMethods);
                }
                //if (DEBUG) {
                //    String classPath = theCalss.getName();
                //    for (Method m : theMethods) {
                //        Log.d(TAG, classPath + ":" + m.getName());
                //    }
                //}
            }
            if( !getSuper ) {
                break;
            }
            maxSuper--;
            if( maxSuper <= 0 ) {
                break;
            }
            theCalss = theCalss.getSuperclass();
        }

        if (DEBUG && allMethods != null ) {
            int cnt = 0;
            for (Method m : allMethods) {
                Log.d(TAG,"" + cnt + ":" + m.getName());
                cnt++;
            }
        }
        return allMethods;
    }
    public static Method GetClassPublicMethodClass(Class<?> theCalss, String methodName) {
        Method method = null;
        if( theCalss == null ){
            return method;
        }
        Method[] allMethods = GetClassPublicMethodsClass(theCalss,true,true,MAX_SUPER_LEVEL);
        if( allMethods == null ) {
            return method;
        }

        for( Method m : allMethods) {
            //Log.d(TAG, classPath+":" + m.getName());
            if( m.getName().equals(methodName) ) {
                method = m;
                break;
            }
        }

        //method = theCalss.getMethod(methodName, String.class);    // "getService"
        //if (method != null) {
        //    method.setAccessible(true);
        //}
        //binder = (IBinder) method.invoke(null,
        //new Object[]{"motionCtrlService"});  // NoSuchMethodException

        return method;
    }

    public static Method GetClassAllMethodClass( Class<?> theCalss, String methodName) {
        Method method = null;
        if( theCalss == null ){
            return method;
        }
        Method[] allMethods = GetClassPublicMethodsClass(theCalss,true,false,MAX_SUPER_LEVEL);
        if( allMethods == null ) {
            return method;
        }

        for( Method m : allMethods) {
            //Log.d(TAG, classPath+":" + m.getName());
            if( m.getName().equals(methodName) ) {
                method = m;
                break;
            }
        }

        //method = theCalss.getMethod(methodName, String.class);    // "getService"
        if (method != null) {
            method.setAccessible(true);
        }
        //binder = (IBinder) method.invoke(null,
        //new Object[]{"motionCtrlService"});  // NoSuchMethodException
        return method;
    }

    public static Method GetClassPublicMethod(String classPath, String methodName) {
        Method m = null;
        try {
            Class<?> theCalss = Class.forName(classPath);
            m = GetClassPublicMethodClass(theCalss,methodName);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            m = null;
        }
        return m;
    }

    public static Method GetClassAllMethod(String classPath, String methodName) {
        Method m = null;
        try {
            Class<?> theCalss = Class.forName(classPath);
            m = GetClassAllMethodClass(theCalss,methodName);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            m = null;
        }
        return m;
    }

    public static Object invokeMethod(Method m, Object receiver, Object... args) {
        Object retObject = null;
        try {
            retObject = m.invoke(receiver, args);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return retObject;
    }

    public static Object invokeMethodArgs(Method m, Object... args) {
        Object retObject = null;
        try {
            retObject = m.invoke(null, args);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return retObject;
    }
}
